﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Bamboo.Utils;
using System.IO;
using System.Reflection;
using System.Net.Mail;

namespace Bamboo.ExeShell
{
    public class ScriptHelper
    {
        public ScriptHelper(ICommonLog view)
        {
            _view = view;
            GBK = Encoding.GetEncoding("gbk");
        }

        private ICommonLog _view;

        /// <summary>
        /// 各种超时时间，默认10小时
        /// </summary>
        public int TimeoutSeconds = 3600 * 10;

        public string LogMethod(Type type, string methodName, params object[] paramValues)
        {
            var m = type.GetMethod(methodName);
            var s = new StringBuilder(4096);
            s.Append(m.Name + ": ");
            bool first = true;
            var parameters = m.GetParameters();
            if (parameters.Length != paramValues.Length) throw new Exception(
                "方法的参数个数与传入的不匹配：" + type.FullName + "." + m.Name);
            for (int i = 0; i < parameters.Length; ++i)
            {
                var item = parameters[i];
                var lowerName = item.Name.ToLower();
                bool masked = lowerName.Contains("password") || lowerName.Contains("pass") || lowerName.Contains("pwd");
                if (first) first = false;
                else s.Append(", ");
                var paramValue = i < paramValues.Length ? paramValues[i] : "(null)";
                if (paramValue == null) paramValue = "(null)";
                string value = masked ? "(加密)" : paramValue.ToString();
                s.Append(item.Name + "=" + value);
            }
            string text = s.ToString();
            _view.Log(text);
            return text;
        }

        public string LogMethod(string methodName, params object[] paramValues)
        {
            return LogMethod(this.GetType(), methodName, paramValues);
        }

        public Encoding GBK { get; private set; }

        /// <summary>
        /// 检查输入文本是否符合正则表达式，不符合就报错
        /// 可以定制错误信息
        /// </summary>
        /// <param name="input"></param>
        /// <param name="regex_pattern"></param>
        /// <param name="errorMessage"></param>
        public void Check(string input, string regex_pattern, string errorMessage)
        {
            bool matched = System.Text.RegularExpressions.Regex.IsMatch(input, regex_pattern);
            if (!matched) throw new Exception(errorMessage + " (当前值：" + input + ")");
        }

        public void CheckNull(string input, string name)
        {
            if (string.IsNullOrWhiteSpace(input)) throw new Exception("参数为空：" + name);
        }

        /// <summary>
        /// 获取一个进程执行的实例
        /// </summary>
        /// <returns></returns>
        public ProcessExecute GetProcess()
        {
            var obj = new ProcessExecute();
            obj.DebugMode = true;
            obj.OutputHandler += _view.Log;
            obj.ErrorHandler += _view.LogError;
            obj.EncodingName = "gbk";
            obj.TimeoutSeconds = this.TimeoutSeconds;
            return obj;
        }
        /// <summary>
        /// 执行命令
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="args"></param>
        /// <param name="workDir"></param>
        /// <param name="ignoreError"></param>
        /// <returns></returns>
        public ProcessExecute RunProcess(string filename, string args, string workDir, int timeoutSeconds, bool ignoreError)
        {
            LogMethod(nameof(RunProcess), filename, args, workDir, timeoutSeconds, ignoreError);
            var proc = GetProcess();
            proc.FileName = filename;
            proc.Arguments = args;
            proc.WorkingDirectory = workDir;
            proc.IgnoreError = ignoreError;
            proc.TimeoutSeconds = timeoutSeconds;
            proc.Run();
            return proc;
        }
        /// <summary>
        /// 执行批处理命令
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="workDir"></param>
        /// <param name="ignoreError"></param>
        /// <returns></returns>
        public ProcessExecute RunBat(string cmd, bool ignoreError = true, 
            string logfile = "", string errorfile = "", string workingPath = "", int timeoutSeconds = 36000)
        {
            LogMethod(nameof(RunBat), cmd, ignoreError, logfile, errorfile, workingPath, timeoutSeconds);
            var proc = GetProcess();
            proc.IgnoreError = ignoreError;
            if (!string.IsNullOrWhiteSpace(workingPath)) proc.WorkingDirectory = workingPath;
            if (!string.IsNullOrWhiteSpace(logfile)) proc.OutputFileName = logfile;
            if (!string.IsNullOrWhiteSpace(errorfile)) proc.ErrorFileName = errorfile;
            proc.TimeoutSeconds = timeoutSeconds;
            proc.RunBat(cmd);
            return proc;
        }

        Bamboo.ExeShell.Pscp GetPscp(string ip, string user, string pass)
        {
            var v = new Bamboo.ExeShell.Pscp();
            v.Hostname = ip;
            v.Username = user;
            v.Password = Bamboo.Utils.Encrypt.DES3Decrypt(pass);
            v.LogHandler += _view.Log;
            v.ErrorHandler += _view.LogError;
            v.IgnoreError = false;
            return v;
        }

        /// <summary>
        /// 使用pscp通过scp协议上传本地文件到linux服务器
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="user"></param>
        /// <param name="pass"></param>
        /// <param name="local_file">本地文件</param>
        /// <param name="remote_dir">远端目录</param>
        /// <param name="thread"></param>
        public void PscpUploadfile(string ip, string user, string pass, string local_file, string remote_dir)
        {
            LogMethod(nameof(PscpUploadfile), ip, user, pass, local_file, remote_dir);
            if (!File.Exists(local_file))
                throw new Exception("本地文件不存在：" + local_file);

            var pscp = GetPscp(ip, user, pass);
            pscp.UploadFile(local_file, remote_dir);
        }

        public Bamboo.ExeShell.Plink CreatePlink(string ip, string user, string pass)
        {
            var v = new Bamboo.ExeShell.Plink();
            v.Hostname = ip;
            v.Username = user;
            v.Password = Bamboo.Utils.Encrypt.DES3Decrypt(pass);
            v.IgnoreError = false;
            v.LogHandler += _view.Log;
            v.ErrorHandler += _view.LogError;
            v.TimeoutSeconds = this.TimeoutSeconds;
            return v;
        }

        /// <summary>
        /// 使用plink连接SSH协议的linux服务器，执行命令并显示结果
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="user"></param>
        /// <param name="pass"></param>
        /// <param name="cmd">待执行的命令</param>
        /// <param name="ignore_error">遇到错误输出时：true-提示并继续运行，false-抛出异常</param>
        /// <param name="outfile"></param>
        /// <param name="errorfile"></param>
        public void Plink(string ip, string user, string pass, string cmd, bool ignore_error, string outfile, string errorfile)
        {
            LogMethod(nameof(Plink), ip, user, pass, cmd, ignore_error, outfile, errorfile);
            var p = CreatePlink(ip, user, pass);
            p.IgnoreError = ignore_error;
            if (!string.IsNullOrWhiteSpace(outfile)) p.OutFilename = outfile;
            if (!string.IsNullOrWhiteSpace(errorfile)) p.ErrorFilename = errorfile;
            p.ExecuteCommand(cmd);
        }

        /// <summary>
        /// 运行SVN命令
        /// </summary>
        /// <param name="param"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="ignoreError"></param>
        /// <returns></returns>
        public ProcessExecute RunSvn(string param, string username, string password, bool ignoreError)
        {
            LogMethod(nameof(RunSvn), param, username, password, ignoreError);
            var proc = GetProcess();
            proc.FileName = "svn.exe";
            proc.EncodingName = "gbk";
            proc.Arguments = param + Bamboo.ExeShell.Svn.GetCommonParam(username, password);
            proc.IgnoreError = ignoreError;
            proc.MaskWords.Add("--password " + password);
            proc.DebugMode = false;
            proc.Run();
            return proc;
        }

        /// <summary>
        /// 更新SVN本地目录
        /// </summary>
        /// <param name="url"></param>
        /// <param name="path"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="revision">-1代表最新版本</param>
        public void SvnUpdate(string url, string path, string username, string password, int revision = -1)
        {
            LogMethod(nameof(SvnUpdate), url, path, username, password, revision);
            if (revision >= 0)
            {
                //检查传入版本号是否有效
                var serverRevision = GetSvnRevision(url, username, password);
                if (serverRevision < revision)
                {
                    _view.Log("服务器上的版本号比传入的小，自动修正版本号：" + revision + "=>" + serverRevision);
                    revision = serverRevision;
                }
            }
            var sw = System.Diagnostics.Stopwatch.StartNew();
            string paramStyle1 = revision > 0 ? "-r " + revision : "";
            string paramStyle2 = revision > 0 ? "@" + revision : "";

            var proc = RunSvn("cleanup -q --remove-unversioned \"" + path + "\"", username, password, true);
            if (proc.ExitCode == 0)
            {
                proc = RunSvn("revert -q -R \"" + path + "\"", username, password, true);
            }
            if (proc.ExitCode == 0)
            {
                proc = RunSvn("update -q " + paramStyle1 + " \"" + path + "\"", username, password, true);
            }
            if (proc.ExitCode != 0 && proc.ErrorBuffer.ToString().Contains("E160005"))
            {  //[114510][error] svn: E160005: Target path '/branches/FM_20200119/dev/sdk' does not exist
                _view.Log("[svn_update] 尝试去除版本重新签出");
                proc = RunSvn("update -q \"" + path + "\"", username, password, true);
            }
            if (proc.ExitCode != 0)
            {   
                _view.Log("[svn_update] 更新失败，尝试删除目录重新签出");
                if (Directory.Exists(path))
                {
                    bool succ = false;
                    for (int i = 1; i <= 5; ++i)
                    {
                        try
                        {
                            RebuildDir(path);
                            succ = true;
                            break;
                        }
                        catch (Exception ex)
                        {
                            _view.Log("[svn_update]删除失败：" + ex.ToString());
                            System.Threading.Thread.Sleep(5000);
                        }
                    }
                    if (!succ) throw new Exception("尝试5次删除目录均失败：" + path);
                }
                    
                proc = RunSvn("co -q \"" + url + paramStyle2 + "\" \"" + path + "\"", username, password, true);
            }
            if (proc.ExitCode != 0)
            {
                if (proc.OutputBuffer.ToString().Contains("E170000"))
                {
                    _view.Log("检测到错误E170000，可能版本号太大，尝试去掉版本号重新签出。");
                    proc = RunSvn("co -q \"" + url + "\" \"" + path + "\"", username, password, false);
                }
            }
            RunSvn("info \"" + path + "\"", username, password, false);
            sw.Stop();
            _view.Log("[svn_update] 执行成功，耗时：" + sw.Elapsed.ToString());
        }
        /// <summary>
        /// 获取SVN INFO执行的结果
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public string GetSvnInfo(string uri, string username, string password)
        {
            LogMethod(nameof(GetSvnInfo), uri, username, password);
            var proc = RunSvn("info " + uri, username, password, false);
            return proc.OutputBuffer.ToString().Trim();
        }
        /// <summary>
        /// 获取指定SVN本地目录或远端URL的最新版本号
        /// （svn info --show-item ast-changed-revision）
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public int GetSvnRevision(string uri, string username, string password)
        {
            LogMethod(nameof(GetSvnRevision), uri, username, password);
            var proc = RunSvn("info " + uri + " --show-item last-changed-revision", username, password, false);
            var text = proc.OutputText.ToString().Trim();
            int revision = int.Parse(text);
            return revision;
        }
        /// <summary>
        /// 获取SVN仓库的最新版本号，可能比传入的uri版本号更大
        /// （svn info --show-item revision）
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public int GetSvnRepoRevision(string uri, string username, string password)
        {
            LogMethod(nameof(GetSvnRevision), uri, username, password);
            var proc = RunSvn("info " + uri + " --show-item revision", username, password, false);
            var text = proc.OutputText.ToString().Trim();
            int revision = int.Parse(text);
            return revision;
        }
        /// <summary>
        /// 删除本地文件，若不存在则略过
        /// </summary>
        /// <param name="path"></param>
        /// <param name="pattern"></param>
        /// <param name="recursive"></param>
        /// <param name="thread"></param>
        public void RemoveFiles(string path, string pattern, bool recursive)
        {
            LogMethod(nameof(RemoveFiles), path, pattern, recursive);

            DirectoryInfo dir = new DirectoryInfo(path);
            if (!dir.Exists)
                return;
            SearchOption option = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
            string[] filenames = Directory.GetFiles(path, pattern, option);
            foreach (string filename in filenames)
            {
                File.Delete(filename);
                _view.Log("删除本地文件：" + filename);
            }
        }
        /// <summary>
        /// 复制文件，如果目标文件已存在，自动覆盖。
        /// </summary>
        /// <param name="srcdir"></param>
        /// <param name="pattern"></param>
        /// <param name="destdir"></param>
        /// <param name="recursive"></param>
        /// <param name="overwrite">目标文件存在时是否覆盖</param>
        public void CopyFiles(string srcdir, string pattern, string destdir, bool recursive, bool overwrite)
        {
            LogMethod(nameof(CopyFiles), srcdir, pattern, destdir, recursive, overwrite);

            var merger = new Bamboo.Utils.PathMerger();
            merger.SrcBaseDir = new DirectoryInfo(srcdir);
            merger.DestBaseDir = new DirectoryInfo(destdir);
            merger.LogErrorHandler += _view.LogError;
            SearchOption op = (recursive) ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
            merger.CopyFiles(srcdir, pattern, op, destdir, overwrite);
        }

        public void RebuildDir(string path)
        {
            LogMethod(nameof(RebuildDir), path);
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
                return;
            }

            bool hasError = false;
            var batContent = new StringBuilder(128 * 1024);
            foreach (var item in Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly))
            {
                try
                {
                    Directory.Delete(item, true);
                }
                catch (Exception ex)
                {
                    _view.Log("删除目录失败：" + item + "，错误信息："+ ex.ToString());
                    batContent.AppendLine("rd /s /q \"" + item + "\"");
                }
            }
            foreach (var item in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
            {
                try
                {
                    File.Delete(item);
                }
                catch (Exception ex)
                {
                    _view.Log("删除文件失败：" + item + "，错误信息："+ ex.ToString());
                    batContent.AppendLine("del \"" + item + "\"");
                }
            }

            if (batContent.Length > 0)
            {
                _view.Log("使用批处理尝试二次删除");
                var proc = new ProcessExecute();
                proc.IgnoreError = false;
                proc.OutputHandler += _view.LogDetail;
                proc.ErrorHandler += _view.LogError;
                proc.RunBat(batContent.ToString());
            }

            var remainItems = new StringBuilder(128*1024);
            foreach (var item in Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly))
            {
                remainItems.AppendLine(item);
            }
            foreach (var item in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
            {
                remainItems.AppendLine(item);
            }
            if (remainItems.Length > 0)
            {
                throw new Exception("清空目录失败，以下子目录或文件删除失败：\r\n" + remainItems);
            }
            _view.Log("清空目录成功：" + path);
        }

        /// <summary>
        /// 编译VisualStudio解决方案
        /// 要求配置Path环境变量
        /// </summary>
        /// <param name="slnFile"></param>
        /// <param name="rebuild"></param>
        /// <param name="devenvPath">传空程序会自己找</param>
        /// <param name="platform">平台，例如：Debug|Any CPU</param>
        public void BuildSln(string slnFile, bool rebuild, string platform, string devenvPath)
        {
            LogMethod(nameof(BuildSln), slnFile, rebuild, platform, devenvPath);
            var slnFileInfo = new FileInfo(slnFile);
            var logFileInfo = new FileInfo(Path.Combine(slnFileInfo.DirectoryName,
                Path.GetFileNameWithoutExtension(slnFile) + ".build.log"));

            if (!File.Exists(slnFileInfo.FullName))
            {
                _view.Log("解决方案文件不存在，不编译：" + slnFileInfo.FullName);
                return;
            }
            if (string.IsNullOrWhiteSpace(devenvPath)) devenvPath = "devenv.exe";
            
            string p1 = rebuild ? " /rebuild " : " /build ";
            string param = slnFile + p1 + " \"" + platform + "\" /out \"" + logFileInfo.FullName + "\"";
            try
            {
                RunProcess(devenvPath, param, "", 3600, false);
            }
            catch (Exception ex)
            {
                _view.LogError("编译报错：" + ex.ToString());
                //检查错误
                var check = new Bamboo.Utils.TextFileChecker();
                check.Init();
                check.AddErrorKey("error CS");
                check.AddErrorKey("error ");
                check.AddFile(logFileInfo.FullName);
                check.Execute();
                throw ex;   //一般走不到这里
            }
        }
        /// <summary>
        /// 使用curl.exe上传单个文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="remotePath"></param>
        /// <param name="ip"></param>
        /// <param name="user"></param>
        /// <param name="password"></param>
        /// <param name="port"></param>
        public void CurlUploadFile(string path, string remotePath, string ip, string user, string password, int port = 21)
        {
            LogMethod(nameof(CurlUploadFile), path, remotePath, ip, user, password, port);
            var proc = GetProcess();
            proc.FileName = "curl.exe";
            proc.Arguments = "--ftp-create-dirs -u " + user + ":" + password + " -T \"" +
               path + "\" \"ftp://" + ip + ":" + port + "/" + remotePath.Trim('/') + "/\"";
            proc.DebugMode = false;
            proc.MaskWords.Add(user + ":" + password);
            proc.IgnoreError = false;
            proc.Run();
        }

        /// <summary>
        /// 获取升级包名字
        /// </summary>
        /// <param name="prefix">前缀，例如fmserver</param>
        /// <param name="branchName">分支版本号，例如trunk、20191018</param>
        /// <param name="clientName">客户名称，可传空</param>
        /// <param name="date">日期时间，可传空</param>
        /// <param name="revision">SVN版本号，小于等于0代表没有</param>
        /// <param name="postfix">后缀，例如.zip、.7z</param>
        /// <returns></returns>
        public string GetPackName(string prefix, string branchName, string clientName, DateTime date, int revision, string postfix)
        {
            string branchText = string.IsNullOrWhiteSpace(branchName) ? "" : "_" + branchName.ToLower();
            string clientText = string.IsNullOrWhiteSpace(clientName) ? "" : "." + clientName.ToLower();
            string dateText = (date == null) ? "" : "." + date.ToString("yyyyMMdd.HHmm");
            string revText = revision > 0 ? ".r" + revision : "";
            string ret = prefix.ToLower().Trim() + branchText.Trim() + clientText.Trim() + dateText.Trim() + revText.Trim() + postfix.Trim();
            _view.Log("升级包文件名：" + ret);
            return ret;
        }

        /// <summary>
        /// 根据文件夹的创建日期，保留指定数目的比较新的文件夹，删除旧的
        /// </summary>
        /// <param name="path"></param>
        /// <param name="pattern"></param>
        /// <param name="remainCount"></param>
        public void RemoveOldDirectories(string path, string pattern, int remainCount)
        {
            LogMethod(nameof(RemoveOldDirectories), path, pattern, remainCount);
            DirectoryInfo basedir = new DirectoryInfo(path);
            if (!basedir.Exists)
                return;
            DirectoryInfo[] subdirs = basedir.GetDirectories(pattern, SearchOption.TopDirectoryOnly);
            if (subdirs.Length <= remainCount)
            {
                _view.Log("文件夹数量太少，不必删除。保留个数：" + remainCount + "，当前目录个数：" + subdirs.Length);
                return;
            }
            List<DirectoryInfo> dirs = new List<DirectoryInfo>(subdirs);
            dirs.Sort((a, b) => { //升序排序
                if (a.CreationTime > b.CreationTime) return 1;
                else if (a.CreationTime < b.CreationTime) return -1;
                else return 0;
            });
            int removeCount = dirs.Count - remainCount;
            _view.Log("合计找到文件夹：" + dirs.Count + "个，保留：" + remainCount + "个，删除：" + removeCount + "个");
            for (int i = 0; i < removeCount; i++)
            {
                var d = dirs[i];
                _view.Log("正在删除文件夹：" + d.FullName + "，创建日期：" + d.CreationTime);
                d.Delete(true);
            }
        }
        /// <summary>
        /// 根据文件名中的.yyyyMMdd.HHmm或文件修改时间，删除旧的文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="pattern"></param>
        /// <param name="recursive"></param>
        /// <param name="remain1"></param>
        /// <param name="remain2"></param>
        /// <param name="remain3"></param>
        public void RemoveOldFiles(string path, string pattern, bool recursive, int remain1, int remain2, int remain3)
        {
            LogMethod(nameof(RemoveOldFiles), path, pattern, recursive, remain1, remain2, remain3);
            var roller = new FileRoller();
            roller.RemainCount1 = remain1;
            roller.RemainCount2 = remain2;
            roller.RemainCount3 = remain3;
            roller.AddFileFromLocalPath(path, pattern, recursive);
            roller.Execute();
            var s = roller.GetDetailInfo();
            _view.Log(s);
        }

        /// <summary>
        /// 发邮件
        /// </summary>
        /// <param name="subject"></param>
        /// <param name="body"></param>
        /// <param name="receiverFile">保存收件人地址的文本文件</param>
        /// <param name="attachments">附件，可以传null</param>
        public void SendMail(string smtpHost, int smtpPort, string smtpUser, string smtpPassword, 
            string subject, string body, string receiverFile, string[] attachments)
        {
            LogMethod(nameof(SendMail), smtpHost, smtpPort, smtpUser, smtpPassword, subject, body, receiverFile, attachments);
            //读取收件人列表
            if (!File.Exists(receiverFile))
            {
                _view.Log("没有收件人列表文件，不发送邮件：" + receiverFile);
                return;
            }
            var addressArray = File.ReadAllLines(receiverFile, this.GBK);
            if (addressArray.Length == 0)
            {
                _view.Log("收件人列表为空，不发送邮件：" + receiverFile);
                return;
            }

            using (var mail = new MailMessage())
            {
                mail.Subject = subject;
                mail.Body = body;
                mail.IsBodyHtml = false;
                if (attachments != null)
                {
                    foreach (var item in attachments)
                    {
                        if (File.Exists(item)) mail.Attachments.Add(new Attachment(item));
                    }
                }
                mail.From = new MailAddress(smtpUser, "");
                foreach (var item in addressArray)
                {
                    string address = "", name = "";
                    if (item.Contains("|"))
                    {
                        var parts = item.Split('|');
                        if (parts.Length != 2)
                        {
                            address = parts[0];
                        }
                        else if (parts[0].Contains("@"))
                        {
                            address = parts[0];
                            name = parts[1];
                        }
                        else
                        {
                            address = parts[1];
                            name = parts[0];
                        }
                    }
                    else
                    {
                        address = item;
                    }
                    address = address.Trim();
                    name = name.Trim();
                    if (name != "") mail.To.Add(new MailAddress(address, name));
                    else mail.To.Add(new MailAddress(address));
                }
                using (var smtp = new SmtpClient())
                {
                    smtp.Host = smtpHost;
                    smtp.Port = smtpPort;
                    smtp.EnableSsl = false;
                    smtp.UseDefaultCredentials = false;
                    smtpPassword = Bamboo.Utils.Encrypt.DES3Decrypt(smtpPassword);
                    smtp.Credentials = new System.Net.NetworkCredential(smtpUser, smtpPassword);
                    smtp.Send(mail);
                    _view.Log("发送邮件成功。");
                }
            }
        }

        /// <summary>
        /// 拼接Oracle数据库连接串
        /// </summary>
        /// <param name="hostName">主机名或IP</param>
        /// <param name="port">端口号，默认端口是1521</param>
        /// <param name="serviceName">服务名</param>
        /// <param name="userName">用户名</param>
        /// <param name="password">密码</param>
        /// <returns></returns>
        public string GetOracleConnectionString(string hostName, int port, string serviceName, string userName, string password)
        {
            string s = string.Format("User Id={0};Password={1};" +
                "Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST={2})(PORT={3})))(CONNECT_DATA=(SERVICE_NAME={4})))",
                userName, password, hostName, port, serviceName);
            return s;
        }

        /// <summary>
        /// 普通文本转Html文本（增加转义）
        /// </summary>
        /// <param name="input">input</param>
        /// <returns></returns>
        public static string HtmlToEsc(string input)
        {
            if (string.IsNullOrEmpty(input)) { return ""; }

            input = input.Replace("&", "&amp;")
                        .Replace("'", "&#39;")
                        .Replace("\"", "&quot;")
                        .Replace("<", "&lt;")
                        .Replace(">", "&gt;")
                        .Replace(" ", "&nbsp;")
                        .Replace("©", "&copy;")
                        .Replace("®", "&reg;")
                        .Replace("™", "&#8482;");
            return input;
        }

        /// <summary>
        /// HTML文本转普通文本（取消转义）
        /// </summary>
        /// <param name="input">input</param>
        /// <returns></returns>
        public static string EscToHtml(string input)
        {
            if (string.IsNullOrEmpty(input)) { return ""; }

            input = input.Replace("&#8482;", "™")
                        .Replace("&reg;", "®")
                        .Replace("&copy;", "©")
                        .Replace("&nbsp;", " ")
                        .Replace("&gt;", ">")
                        .Replace("&lt;", "<")
                        .Replace("&quot;", "\"")
                        .Replace("&#39;", "'")
                        .Replace("&amp;", "&");
            return input;
        }
    }
}
